using BepuUtilities.Collections;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;

namespace BepuUtilities.Memory
{
    /// <summary>
    /// 管理标识符值池。从池中抓取ID将挑选之前已经挑选并返回的号码,
    /// 或者,如果这些都不可用,则取大于任何现有ID的最小值。
    /// </summary>
    public struct IdPool
    {
        // TODO：在请求然后返回大量ID的情况下,您可以提高内存效率。
        // 不过,这将需要更多的簿记工作;提取或退还ID都可能会稍微慢一点。

        // 请注意,所有可用的ID都保证小于nextIndex。
        // [0,nextIndex)包含所有当前使用的ID和AvailableIds中包含的ID。
        private int nextIndex;

        int availableIdCount;
        Buffer<int> availableIds;

        /// <summary>
        /// 获取到目前为止声明的任何索引可能具有的最高值。
        /// 这不一定是当前最高的索赔指数;该值可能代表已发布的较早索赔。
        /// -1\f25 Not-1\f6(无人认领)。
        /// </summary>
        public int HighestPossiblyClaimedId
        {
            get { return nextIndex - 1; }
        }

        /// <summary>
        /// 获取池中等待的以前返回的ID数。
        /// </summary>
        public int AvailableIdCount => availableIdCount;

        public int Capacity => availableIds.Length;

        /// <summary>
        /// 获取id池是否有分配给它的后备资源以及是否可以使用。
        /// </summary>
        public bool Allocated => availableIds.Allocated;

        public IdPool(int initialCapacity, IUnmanagedMemoryPool pool)
        {
            nextIndex = 0;
            availableIdCount = 0;
            pool.TakeAtLeast(initialCapacity, out availableIds);
        }


        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public int Take()
        {
            Debug.Assert(availableIds.Allocated);
            if (availableIdCount > 0)
                return availableIds[--availableIdCount];
            return nextIndex++;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void Return(int id, IUnmanagedMemoryPool pool)
        {
            Debug.Assert(availableIds.Allocated);
            if (availableIdCount == availableIds.Length)
            {
                var oldAvailableIds = availableIds;
                pool.TakeAtLeast(Math.Max(availableIdCount * 2, availableIds.Length), out availableIds);
                oldAvailableIds.CopyTo(0, availableIds, 0, availableIdCount);
                pool.Return(ref oldAvailableIds);
            }
            ReturnUnsafely(id);
        }

        /// <summary>
        /// 将id返回到池,而不检查是否需要调整可用id堆栈的大小。
        /// </summary>
        /// <param name="id">要返回的ID。</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void ReturnUnsafely(int id)
        {
            Debug.Assert(availableIds.Allocated && availableIds.Length > availableIdCount);
            availableIds[availableIdCount++] = id;
        }

        /// <summary>
        /// 重置IdPool。
        /// </summary>
        public void Clear()
        {
            nextIndex = 0;
            availableIdCount = 0;
        }

        void InternalResize(int newSize, IUnmanagedMemoryPool pool)
        {
            var oldAvailableIds = availableIds;
            pool.TakeAtLeast(newSize, out availableIds);
            Debug.Assert(oldAvailableIds.Length != availableIds.Length, "Did you really mean to resize this? Nothing changed!");
            oldAvailableIds.CopyTo(0, availableIds, 0, availableIdCount);
            pool.Return(ref oldAvailableIds);
        }

        /// <summary>
        /// 确保基础ID队列可以容纳至少一定数量的ID。
        /// </summary>
        /// <param name="count">要在可用ID队列中为其预分配空间的元素数。</param>
        /// <param name="pool">要从中拉出调整大小的跨度的池。</param>
        public void EnsureCapacity(int count, IUnmanagedMemoryPool pool)
        {
            if (!availableIds.Allocated)
            {
                // 如果这被处理了,我们必须明确地给它补水。
                this = new IdPool(count, pool);
            }
            else
            {
                if (availableIds.Length < count)
                    InternalResize(count, pool);
            }
        }

        /// <summary>
        /// 将可用ID队列缩小到可以满足给定计数和当前可用ID计数的最小大小。
        /// </summary>
        /// <param name="minumCount">可用ID队列中保证空间的元素数。</param>
        public void Compact(int minimumCount, IUnmanagedMemoryPool pool)
        {
            Debug.Assert(availableIds.Allocated);
            var targetLength = BufferPool.GetCapacityForCount<int>(Math.Max(minimumCount, availableIdCount));
            if (availableIds.Length > targetLength)
            {
                InternalResize(targetLength, pool);
            }
        }

        /// <summary>
        /// 将基础缓冲区大小调整为保存给定计数和当前可用id计数所需的最小大小。
        /// </summary>
        /// <param name="count">可用ID队列中保证空间的元素数。</param>
        public void Resize(int count, IUnmanagedMemoryPool pool)
        {
            if (!availableIds.Allocated)
            {
                // 如果这被处理了,我们必须明确地给它补水。
                this = new IdPool(count, pool);
            }
            else
            {
                var targetLength = BufferPool.GetCapacityForCount<int>(Math.Max(count, availableIdCount));
                if (availableIds.Length != targetLength)
                {
                    InternalResize(targetLength, pool);
                }
            }
        }

        /// <summary>
        /// 将基础内存返回到池。
        /// </summary>
        /// <remarks>The IdPool can be reused only if EnsureCapacity or Resize is called.</remarks>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void Dispose(IUnmanagedMemoryPool pool)
        {
            pool.Return(ref availableIds);
            // 这简化了重用,并使使用无效数据变得更加困难。
            this = default;
        }
    }
}
